package org.de.maeyer.statistic.view.swt;

import static java.lang.Math.*;

import java.util.List;

import org.apache.commons.lang.ArrayUtils;
import org.de.maeyer.statistic.Category;
import org.de.maeyer.statistic.Statistics;
import org.de.maeyer.statistic.view.View;
import org.de.maeyer.statistic.view.ViewConfiguration;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Shell;

/**
 * @author Peter De Maeyer
 */
public class BarChart implements View {

	private final int spacing = 4;

	private final Shell shell;

	private final ViewConfiguration config;

	public BarChart(Shell shell, ViewConfiguration config) {
		this.shell = shell;
		this.config = config;
	}

	@Override
	public void paint(PaintEvent event, Statistics statistics) {
		if (statistics.hasHistogram()) {
			paintHistogram(event, statistics);
		} else {
			paintText(event, "No histogram data: " + statistics.getName());
		}
	}

	private void paintText(PaintEvent event, String text) {
		Rectangle clientArea = shell.getClientArea();
		Point textExtent = event.gc.textExtent(text);
		event.gc.drawText(text, (clientArea.width - textExtent.x) / 2, (clientArea.height - textExtent.y) / 2);
	}

	private void paintHistogram(PaintEvent event, Statistics statistics) {
		List<Category> categories = statistics.getCategories();
		int[] modeCategoryIndexes = statistics.getModeCategoryIndexes();
		double modeDensity = modeCategoryIndexes.length > 0 ? categories.get(modeCategoryIndexes[0]).getDensity() : 1.;
		Rectangle clientArea = shell.getClientArea();
		final int n = categories.size();
		int maxTextHeight = 0;
		for (Category category: categories) {
			int textHeight = max(event.gc.textExtent(String.valueOf(category.getValue())).y, event.gc.textExtent(String.valueOf(category.getCount())).y);
			if (maxTextHeight < textHeight) {
				maxTextHeight = textHeight;
			}
		}
		int i = 0;
		int maxBarHeight = clientArea.height - maxTextHeight * 2 - spacing * 3;
		int barBottomVerticalOffset = clientArea.height - maxTextHeight - spacing * 2 + spacing / 2;
		int valueTextVerticalOffset = clientArea.height - maxTextHeight - spacing + spacing / 2;
		Color defaultBackground = event.gc.getBackground();
		for (Category category: categories) {
			boolean mode = ArrayUtils.indexOf(modeCategoryIndexes, i) != -1;
			// Draw the bar
			Object value = category.getValue();
			double density = category.getDensity();
			int barHorizontalOffset = clientArea.width * i++ / n + spacing / 2;
			int barWidth = clientArea.width * i / n - spacing + spacing / 2 - barHorizontalOffset;
			int barHeight = (int) round(maxBarHeight * density / modeDensity);
			int barVerticalOffset = barBottomVerticalOffset - barHeight;
			Color barColor = mode ? config.getModeColor(value) : config.getColor(value);
			event.gc.setBackground(barColor);
			event.gc.fillRectangle(barHorizontalOffset, barVerticalOffset, barWidth, barHeight);
			// Draw the count text
			String countText = String.valueOf(density);
			int countTextVerticalOffset = barVerticalOffset - maxTextHeight - spacing;
			int countTextHorizontalOffset = barHorizontalOffset + (barWidth - event.gc.textExtent(countText).x) / 2;
			event.gc.setBackground(defaultBackground);
			event.gc.drawText(countText, countTextHorizontalOffset, countTextVerticalOffset);
			// Draw the value text
			String valueText = String.valueOf(value);
			int valueTextHorizontalOffset = barHorizontalOffset + (barWidth - event.gc.textExtent(valueText).x) / 2;
			event.gc.drawText(valueText, valueTextHorizontalOffset, valueTextVerticalOffset);
		}
	}
}
